﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using DualityEngine.Business.Camera;
using DualityEngine.Data;

namespace DualityEngine.Business.GameModel
{
    public class PlayerShip : Ship
    {
        private const float MinimumAltitude = 350.0f;

        /// <summary>
        /// Direction ship is facing.
        /// </summary>
        public Vector3 Direction;

        /// <summary>
        /// Ship's up vector.
        /// </summary>
        public Vector3 Up;
        
        /// <summary>
        /// Ship's right vector.
        /// </summary>
        public Vector3 Right;

        /// <summary>
        /// Full speed at which ship can rotate; measured in radians per second.
        /// </summary>
        private const float RotationRate = 1.5f;

        /// <summary>
        /// Mass of ship.
        /// </summary>
        private const float Mass = 1.0f;

        /// <summary>
        /// Maximum force that can be applied along the ship's direction.
        /// </summary>
        private const float ThrustForce = 24000.0f;

        /// <summary>
        /// Velocity scalar to approximate drag.
        /// </summary>
        private const float DragFactor = 0.97f;

        /// <summary>
        /// Current ship velocity.
        /// </summary>
        public Vector3 Velocity;

        /// <summary>
        /// Ship world transform matrix.
        /// </summary>
        //private Matrix worldmatrix;

        public PlayerShip(Vector3 position, Model model)
            : base(position, model)
        {
            Reset();
        }

        /// <summary>
        /// Restore the ship to its original starting state
        /// </summary>
        public void Reset()
        {
            position = new Vector3(0, MinimumAltitude, 0);
            Direction = Vector3.Forward;
            Up = Vector3.Up;
            Right = Vector3.Right;
            Velocity = Vector3.Zero;
        }

        /// <summary>
        /// Applies a simple rotation to the ship and animates position based
        /// on simple linear motion physics.
        /// </summary>
        public override void Update(GameTime gameTime)
        {
            KeyboardState keyboardState = Keyboard.GetState();
            GamePadState gamePadState = GamePad.GetState(PlayerIndex.One);

            float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;


            // Determine rotation amount from input
            Vector2 rotationAmount = -gamePadState.ThumbSticks.Left;
            if (keyboardState.IsKeyDown(Keys.A))
                rotationAmount.X = 1.0f;
            if (keyboardState.IsKeyDown(Keys.D))
                rotationAmount.X = -1.0f;


            if (Mouse.GetState().LeftButton == ButtonState.Pressed)
            {
                if (Mouse.GetState().X > 0 && Mouse.GetState().X < (ResourceManager.Instance.graphicsdevice.PresentationParameters.BackBufferWidth / 2) - 200)
                {
                    rotationAmount.X = 1.0f;
                }
                if (Mouse.GetState().X < ResourceManager.Instance.graphicsdevice.PresentationParameters.BackBufferWidth && Mouse.GetState().X > (ResourceManager.Instance.graphicsdevice.PresentationParameters.BackBufferWidth / 2) + 200)
                {
                    rotationAmount.X = -1.0f;
                }
                if (Mouse.GetState().Y > 0 && Mouse.GetState().Y < (ResourceManager.Instance.graphicsdevice.PresentationParameters.BackBufferHeight / 2) - 200)
                {
                    rotationAmount.Y = 1.0f;
                }
                if (Mouse.GetState().Y < ResourceManager.Instance.graphicsdevice.PresentationParameters.BackBufferHeight && Mouse.GetState().Y > (ResourceManager.Instance.graphicsdevice.PresentationParameters.BackBufferHeight / 2) + 200)
                {
                    rotationAmount.Y = -1.0f;
                }
            }

            // Scale rotation amount to radians per second
            rotationAmount = rotationAmount * RotationRate * elapsed;

            // Correct the X axis steering when the ship is upside down
            if (Up.Y < 0)
                rotationAmount.X = -rotationAmount.X;


            // Create rotation matrix from rotation amount
            Matrix rotationMatrix =
                Matrix.CreateFromAxisAngle(Right, rotationAmount.Y) *
                Matrix.CreateRotationY(rotationAmount.X);

            // Rotate orientation vectors
            Direction = Vector3.TransformNormal(Direction, rotationMatrix);
            Up = Vector3.TransformNormal(Up, rotationMatrix);

            // Re-normalize orientation vectors
            // Without this, the matrix transformations may introduce small rounding
            // errors which add up over time and could destabilize the ship.
            Direction.Normalize();
            Up.Normalize();

            // Re-calculate Right
            Right = Vector3.Cross(Direction, Up);

            // The same instability may cause the 3 orientation vectors may
            // also diverge. Either the Up or Direction vector needs to be
            // re-computed with a cross product to ensure orthagonality
            Up = Vector3.Cross(Right, Direction);

            float thrustAmount = 0.0f;
            // Determine thrust amount from input
            if (keyboardState.IsKeyDown(Keys.Space))
                thrustAmount = 1.0f;

            // Calculate force from thrust amount
            Vector3 force = Direction * thrustAmount * ThrustForce;


            // Apply acceleration
            Vector3 acceleration = force / Mass;
            Velocity += acceleration * elapsed;

            // Apply psuedo drag
            Velocity *= DragFactor;

            // Apply velocity
            position += Velocity * elapsed;


            // Prevent ship from flying under the ground
            position.Y = Math.Max(position.Y, MinimumAltitude);


            // Reconstruct the ship's world matrix
            worldmatrix = Matrix.Identity;
            worldmatrix.Forward = Direction;
            worldmatrix.Up = Up;
            worldmatrix.Right = Right;
            worldmatrix.Translation = position;

            //Update Camera and Ship
            ((ChaseCamera)CameraManager.Instance.ActiveCamera).chasePosition = this.position;
            ((ChaseCamera)CameraManager.Instance.ActiveCamera).chaseDirection = this.Direction;
            ((ChaseCamera)CameraManager.Instance.ActiveCamera).up = this.Up;

            //Send position to server
            ServerConnector.Instance.SendPlayerInfo(this.position, this.Direction, this.Up, this.Right);
        }

        public override void Draw(GameTime gameTime)
        {
            Matrix[] transforms = new Matrix[model.Bones.Count];
            model.CopyAbsoluteBoneTransformsTo(transforms);

            foreach (ModelMesh mesh in model.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.EnableDefaultLighting();
                    effect.PreferPerPixelLighting = true;
                    effect.World = transforms[mesh.ParentBone.Index] * worldmatrix;
                    effect.View = CameraManager.Instance.ActiveCamera.viewmatrix;
                    effect.Projection = CameraManager.Instance.ActiveCamera.projectionmatrix;
                }
                mesh.Draw();
            }
        }
    }
}
